// =============================================================================
//  STATS - Statistical Analysis Tools, v.0.01
//  Copyright (C) 2012  Marco Vettigli
// -----------------------------------------------------------------------------
//
// This program  is free software:  you can  redistribute it and/or  modify it
// under the terms of the  GNU General Public License as published by the Free
// Software Foundation,  either version 3 of the License,  or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY;  without even  the  implied  warranty  of  MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the  GNU General Public License  for
// more details.
//
// You  should  have received a copy of the  GNU General Public License  along
// with this program.  If not, see <http://www.gnu.org/licenses/>.
//
// == module ===================================================================
//
// SSCATTERPLOT.CPP
// The class SScatterPlot diplays a scatter plot composed by two SDoubles
// arrays.
//
// Author: Marco Vettigli
//
// TODO: write SScatterPlot module documentation
//
// == implementation ===========================================================
//
// TODO: write SScatterPlot implementation documentation
//
// =============================================================================

// == include directives =======================================================

#include <QPainter>
#include <QList>
#include <QPen>
#include <cmath>
#include "sscatterplot.h"

// == constructors and destructors =============================================

StatS::SScatterPlot::SScatterPlot(QWidget *parent) :
    QWidget(parent)
{
    // initialize axis and set their properties
    _xAxis = new StatS::SDoubleAxis(this);
    _yAxis = new StatS::SDoubleAxis(this);
    _yAxis->setAxisOrientation(StatS::SDoubleAxis::Vertical);
    // initialize axis data with null references
    _table = 0;
    _xData = 0;
    _yData = 0;
    // set object default properties
    _title = "Scatter plot";
    _frameBoxState = true;
    _backColor = QColor(255, 255, 255);
    _titleFont = QFont("Helvetica", 11, QFont::Bold);
    // connect axis properties changed signals
    connect(_xAxis, SIGNAL(propertiesChanged()),
            this, SLOT(on_axisPropertiesChanged()));
    connect(_yAxis, SIGNAL(propertiesChanged()),
            this, SLOT(on_axisPropertiesChanged()));

    setLayout();
}

// == getters ==================================================================

StatS::SDoubles * StatS::SScatterPlot::xDataSource() const
{
    return _xData;
}

StatS::SDoubles * StatS::SScatterPlot::yDataSource() const
{
    return _yData;
}

// == setters ==================================================================

bool StatS::SScatterPlot::setDataSources(StatS::SDoubles* x,
                                         StatS::SDoubles* y)
{
    // check if array references are valid
    if (!x || !y) return false;
    if (x->size() != y->size()) return false;
    // set data references
    _xData = x;
    _yData = y;
    // set plot title
    setTitle(QString("%1<->%2")
             .arg(_xData->name())
             .arg(_yData->name()));
    // set axis titles
    _xAxis->setTitle(_xData->name());
    _yAxis->setTitle(_yData->name());
    // set axis properties    
    _xAxis->setRange(_xData);
    _yAxis->setRange(_yData);
    return true;
}

bool StatS::SScatterPlot::setLinkedTable(StatS::STable *table)
{
    // check if table reference is valid
    if (!table) return false;
    _table = table;
    return true;
}

bool StatS::SScatterPlot::setTitle(const QString title)
{
    _title = title;
    return true;
}

// == inspectors ===============================================================

bool StatS::SScatterPlot::isFrameBoxVisible(bool state)
{
    if (_frameBoxState == state) return false;
    _frameBoxState = state;
    return true;
}

// == accessor function definitions ============================================

void StatS::SScatterPlot::paintEvent(QPaintEvent *)
{
    // define pen styles
    QPen standardPen = QPen(Qt::black, 1, Qt::SolidLine);
    QPen majorGridPen = QPen(QColor(200, 200, 200), 1, Qt::SolidLine);
    QPen minorGridPen = QPen(QColor(240, 240, 240), 1, Qt::SolidLine);
    // define QPainter for wifget painting
    QPainter p(this);
    // check if data arrays are linked to SScatterPlot object
    if (!_xData || !_yData) {
        QString warningMessage =
                QString("ERROR(%1): no source data linked")
                .arg(_title);
        p.setFont(_titleFont);
        p.drawText(0, QFontMetrics(_titleFont).height(), warningMessage);
        _xAxis->setVisible(false);
        _yAxis->setVisible(false);
        return;
    }
    // check if plot is linked to a table
    int xCol = -1;
    int yCol = -1;
    if (_table) {
        xCol = _table->getColumnIndex(_xData);
        yCol = _table->getColumnIndex(_yData);
    }
    // draw widget frame with background
    int frameWidth = _xAxis->getFrameSize().width() + _yAxis->getFrameSize().width();
    int frameHeight = _yAxis->getFrameSize().height()
            + _xAxis->getFrameSize().height() + QFontMetrics(_titleFont).height();
    p.fillRect(0, 0, frameWidth, frameHeight, _backColor);
    p.drawRect(0, 0, frameWidth, frameHeight);
    // draw plot title
    p.setFont(_titleFont);
    int xTitle = (_xAxis->getFrameSize().width() - QFontMetrics(_titleFont)
                  .width(_title) + _yAxis->getFrameSize().width() ) / 2 ;
    int yTitle = QFontMetrics(_titleFont).height();
    p.drawText(xTitle, yTitle, _title);
    // reset coordinates system
    p.translate(_yAxis->getFrameSize().width(), _yAxis->getFrameSize().height()
                - _yAxis->getZeroOffset() + QFontMetrics(_titleFont).height()
                + 9);
    p.scale(1,-1);
    // define variables for convenience
    QList<int> minorTicks, majorTicks;
    // draw minor grids if enabled
    p.setPen(minorGridPen);
    if (_xAxis->isMinorGridVisible()) {
        minorTicks = _xAxis->getMinorTicksPosition();
        for (int i = 0; i < minorTicks.size(); i++)
            p.drawLine(minorTicks[i], 0, minorTicks[i], _yAxis->size());
    }
    if (_yAxis->isMinorGridVisible()) {
        minorTicks = _yAxis->getMinorTicksPosition();
        for (int i = 0; i < minorTicks.size(); i++)
            p.drawLine(0, minorTicks[i], _xAxis->size(), minorTicks[i]);
    }
    // draw major grids if enabled
    p.setPen(majorGridPen);
    if (_xAxis->isMajorGridVisible()) {
        majorTicks = _xAxis->getMajorTicksPosition();
        for (int i = 0; i < majorTicks.size(); i++)
            p.drawLine(majorTicks[i], 0, majorTicks[i], _yAxis->size());
    }
    if (_yAxis->isMajorGridVisible()) {
        majorTicks = _yAxis->getMajorTicksPosition();
        for (int i = 0; i < majorTicks.size(); i++)
            p.drawLine(0, majorTicks[i], _xAxis->size(), majorTicks[i]);
    }
    // draw box frame
    p.setPen(standardPen);
    if (_frameBoxState) {
        p.drawLine(0, _yAxis->size(), _xAxis->size(), _yAxis->size());
        p.drawLine(_xAxis->size(), 0, _xAxis->size(), _yAxis->size());
    }    

    bool inX = false;
    bool inY = false;
    QPixmap pixmap;
    p.setPen(QPen(Qt::black,3));    
    for (int i = 0; i < _xData->size(); i++) {
        if (_xData->isNull(i) || _yData->isNull(i)) continue;        
        int x = _xAxis->getValuePosition(_xData->get(i), &inX);
        int y = _yAxis->getValuePosition(_yData->get(i), &inY);
        // check if the element is visible in the plot
        if (!inX || !inY) continue;
        if (_table) {
            pixmap = _table->getRowProperty(i)->getPixmap();
            p.drawPixmap(x - pixmap.width() / 2,
                         y - pixmap.height() / 2,
                         pixmap);
        } else p.drawPoint(x,y);
    }
    return;
}

void StatS::SScatterPlot::setLayout()
{
    // define variables for convenience
    QSize yAxisSize = _yAxis->getFrameSize();
    QSize xAxisSize = _xAxis->getFrameSize();
    QPoint zeroOffset = QPoint(_xAxis->getZeroOffset(), _yAxis->getZeroOffset());
    // set axis positions
    _yAxis->setGeometry(1,QFontMetrics(_titleFont).height() + 10,
                        yAxisSize.width(), yAxisSize.height());
    _xAxis->setGeometry(yAxisSize.width() - zeroOffset.x(),
                        yAxisSize.height() - zeroOffset.y()
                        + QFontMetrics(_titleFont).height() + 9,
                        xAxisSize.width(),
                        xAxisSize.height());

}

void StatS::SScatterPlot::on_axisPropertiesChanged()
{
    setLayout();
    repaint();
}

// == static variables =========================================================
// == static funtion definitions ===============================================
// == private function definitions =============================================
